/*
 * Copyright © 2009 Perseus Project - Tufts University <http://www.perseus.tufts.edu>
 *
 * This file is part of UniCollatorPerseus.
 *
 * OcroPerseus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * OcroPerseus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with OcroPerseus.  If not, see <http://www.gnu.org/licenses/>.
 */

package eu.himeros.ocr.image;

import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.util.Vector;


/**
 * Split image lines in glyphs matrixes.
 * It is used by GlyphIdentifier.
 *
 * @author federico
 */
public class GlyphTokenizer {
    String pixStr = null;
    Vector<Integer> startingPoint = new Vector(100);
    Vector<Integer> endPoint = new Vector(100);
    boolean inGlyph = false;
    int bsum = 0;
    int sp = 0;
    BufferedImage buffImgLine = null;
    BufferedWriter bw = null;
    int[][] data = null;
    static int callnum = 0;
    static int cnt = 0;
    // general params
    int imgW = 0; // image width
    int imgH = 0; // image height
    int lnW = 0;  // fix line width
    int lnH = 0;  // fix line heigth
    int glyphBestW = 0; //glyph typical width
    int leftPen = 0; //max val of penalty on the most left side of the glyph
    int chngPoint = 0; //where penalty stop decreasing and start increasing
    // line params
    int lnX = 0;  // line left upper corner x coord
    int lnY = 0;  // line left upper corner y coord
    int[][] coords = null;

    /**
     *
     * @param bw
     */
    public GlyphTokenizer(BufferedWriter bw) {
        init(bw);
    }

    /**
     *
     * @param bw
     */
    protected void init(BufferedWriter bw) {
        this.bw = bw;
    }

    /**
     *
     * @param imgW
     * @param imgH
     * @param lnW
     * @param lnH
     * @param glyphBestW
     * @param leftPen
     * @param chngPoint
     */
    public void setImageParameters(int imgW, int imgH, int lnW, int lnH, int glyphBestW, int leftPen, int chngPoint) {
        this.imgW = imgW;
        this.imgH = imgH;
        this.lnW = lnW;
        this.lnH = lnH;
        this.glyphBestW = glyphBestW;
        this.leftPen = leftPen;
        this.chngPoint = chngPoint;
    }

    /**
     *
     * @param buffImgLine
     * @param lnX
     * @param lnY
     */
    public void tokenize(BufferedImage buffImgLine, int lnX, int lnY) {
        int pix;
        this.buffImgLine = buffImgLine;
        this.lnX = lnX;
        this.lnY = lnY;
        data = new int[buffImgLine.getWidth()][buffImgLine.getHeight()];
        for (int i = 0; i < buffImgLine.getWidth(); i++) {
            for (int j = 0; j < buffImgLine.getHeight(); j++) {
                pix = ((buffImgLine.getRGB(i, j) == -1) ? 0 : 1);
                data[i][j] = pix;
            }
        }
        makeTokens(data);
    }

    /**
     *
     * @param data
     */
    private void makeTokens(int[][] data) {
        pixStr = null;
        startingPoint = new Vector(100);
        endPoint = new Vector(100);
        inGlyph = false;
        bsum = 0;
        sp = 0;
        for (int i = 0; i < lnW; i++) {
            for (int j = 0; j < lnH; j++) {
                bsum += data[i][j];
            }
            if (bsum > 0 && !inGlyph) {
                startingPoint.add(new Integer(i));
                sp = i;
                inGlyph = true;
            }
            if ((bsum == 0 && inGlyph) || i == lnW - 1) {
                if (i - sp > glyphBestW) {
                    split(sp, i);
                }
                endPoint.add(new Integer(i));
                inGlyph = false;
            }
            bsum = 0;
        }
        bsum = 0;
        makeCoords();
        write();
        callnum++;
    }

    /**
     *
     * @param beg
     * @param end
     */
    private void split(int beg, int end) {
        int w[] = new int[end - beg];
        int marg = 0;
        for (int i = 0; i < w.length; i++) {
            for (int j = 0; j < lnH; j++) {
                marg += data[i + beg][j];
            }
            w[i] = marg;
            marg = 0;
        }
        int wg = leftPen;
        for (int i = 0; i < w.length; i++) {
            w[i] = w[i] + wg;
            if (i < chngPoint) {
                wg--;
            } else if (i > chngPoint) {
                wg++;
            }
        }
        int splitPos = min(w) + beg;
        endPoint.add(new Integer(splitPos));
        startingPoint.add(new Integer(splitPos + 1));
        if (end - splitPos + 1 > glyphBestW) {
            split(splitPos + 1, end);
        }
    }

    /**
     *
     * @param vect
     * @return
     */
    private int min(int[] vect) {
        int minVal = vect[0];
        int pos = 0;
        for (int i = 1; i < vect.length; i++) {
            minVal = Math.min(minVal, vect[i]);
            if (minVal == vect[i]) {
                pos = i;
            }
        }
        return pos;
    }

    /**
     *
     */
    public void makeCoords() {
        int x1 = 0;
        int y1 = 0;
        int x2 = 0;
        int y2 = 0;
        coords = new int[startingPoint.size()][4];
        for (int i = 0; i < startingPoint.size(); i++) {
            x1 = lnX + startingPoint.elementAt(i);
            x2 = lnX + endPoint.elementAt(i);
            y1 = imgH - (lnY + trim(x1 - lnX, x2 - lnX, true));
            //y1=imgH-lnY;
            y2 = imgH - ((lnY + lnH) - trim(x1 - lnX, x2 - lnX, false));
            //y2=imgH-lnY+lnH;
            coords[i][0] = x1;
            coords[i][1] = y1;
            coords[i][2] = x2;
            coords[i][3] = y2;
        }
    }

    /**
     *
     * @return
     */
    public int[][] getCoords() {
        return coords;
    }

    /**
     *
     * @param beg
     * @param end
     * @param upper
     * @return
     */
    private int trim(int beg, int end, boolean upper) {
        int trmLns = 0; //trimmedLines
        int margsum = 0; //marginal sum
        if (upper) {
            outerLoop:
            for (int i = 0; i < lnH; i++) {
                for (int j = beg; j < end; j++) {
                    margsum += data[j][i];
                }
                if (margsum == 0) {
                    trmLns++;
                } else {
                    break outerLoop;
                }
            }
        } else {
            outerLoop:
            for (int i = lnH - 1; i > -1; i--) {
                for (int j = beg; j < end; j++) {
                    margsum += data[j][i];
                }
                if (margsum == 0) {
                    trmLns++;
                } else {
                    break outerLoop;
                }
            }

        }
        return trmLns;
    }

    /**
     *
     */
    private void write() {
        cnt=1;
        for (int i = 0; i < startingPoint.size(); i++) {
            int beg = startingPoint.elementAt(i).intValue();
            int end = endPoint.elementAt(i).intValue();
            if(coords[i][0]>=coords[i][2]) continue;
            try {
                bw.write("" + (callnum+1)+"."+cnt);
                bw.newLine();
                bw.write("@ " + coords[i][0] + " " + coords[i][1] + " " + coords[i][2] + " " + coords[i][3]+" 0");
                bw.newLine();
                cnt++;
            } catch (Exception e) {
                e.printStackTrace();
            }
            boolean emptyLine = false;
            try {
                for (int j = 0; j < lnH; j++) {
                    for (int k = beg; k < end; k++) {
                        for (int l = beg; l < end; l++) {
                            bsum += data[l][j];
                        }
                        if (bsum > 0) {
                            pixStr = ((data[k][j] == 1) ? "#" : ".");
                            bw.write(pixStr);
                        } else {
                            emptyLine = true;
                        }
                        bsum = 0;
                    }
                    if (!emptyLine) {
                        bw.newLine();
                    }
                    emptyLine = false;
                }
                bw.newLine();
                bw.newLine();
                bw.newLine();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            bw.newLine();
            bw.newLine();
            bw.newLine();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
